using System;
using System.Collections;

namespace SMS.SMPPClient.Common
{
    internal class ByteBuilder
    {
        // Fields
        private byte[] _items;
        private int _size;
        private int _version;
        private const int DefaultInitialCapacity = 0x10;

        // Methods
        public ByteBuilder()
        {
            this._items = new byte[0x10];
        }

        public ByteBuilder(int initialCapacity)
        {
            if (initialCapacity < 0)
                throw new ArgumentOutOfRangeException("initialCapacity", initialCapacity, "The initial capacity can't be smaller than zero.");
            
            if (initialCapacity == 0)
                initialCapacity = 0x10;
            
            this._items = new byte[initialCapacity];
        }

        public ByteBuilder(byte[] array)
        {
            this._items = array;
            this._size = array.Length;
        }

        private ByteBuilder(int initialCapacity, bool forceZeroSize)
        {
            if (!forceZeroSize)
                throw new InvalidOperationException("Use MyArrayList(int)");
            
            this._items = null;
        }

        private ByteBuilder(byte[] array, int index, int count)
        {
            this._items = new byte[count];
            if (count == 0)
                this._items = new byte[0x10];
            else
                this._items = new byte[count];
            
            Array.Copy(array, index, this._items, 0, count);
            this._size = count;
        }

        public virtual int Add(byte value)
        {
            if (this._items.Length <= this._size)
                this.EnsureCapacity(this._size + 1);
            
            this._items[this._size] = value;
            this._version++;
            return this._size++;
        }

        public virtual void AddRange(byte[] c)
        {
            if (c != null)
                this.InsertRange(this._size, c);
        }

        public virtual void AddRange(byte[] c, int count)
        {
            byte[] destinationArray = new byte[count];
            Array.Copy(c, 0, destinationArray, 0, count);
            this.InsertRange(this._size, destinationArray);
        }

        public virtual int BinarySearch(byte value)
        {
            int num;
            try
            {
                num = Array.BinarySearch(this._items, 0, this._size, value);
            }
            catch (InvalidOperationException exception)
            {
                throw new ArgumentException(exception.Message);
            }
            return num;
        }

        public virtual int BinarySearch(byte value, IComparer comparer)
        {
            int num;
            try
            {
                num = Array.BinarySearch(this._items, 0, this._size, value, comparer);
            }
            catch (InvalidOperationException exception)
            {
                throw new ArgumentException(exception.Message);
            }
            return num;
        }

        public virtual int BinarySearch(int index, int count, byte value, IComparer comparer)
        {
            int num;
            try
            {
                num = Array.BinarySearch(this._items, index, count, value, comparer);
            }
            catch (InvalidOperationException exception)
            {
                throw new ArgumentException(exception.Message);
            }
            return num;
        }

        internal static void CheckRange(int index, int count, int listCount)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("index", index, "Can't be less than 0.");
            
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", count, "Can't be less than 0.");
            
            if (index > (listCount - count))
                throw new ArgumentException("Index and count do not denote a valid range of elements.", "index");
        }

        public virtual void Clear()
        {
            Array.Clear(this._items, 0, this._size);
            this._size = 0;
            this._version++;
        }

        public virtual object Clone()
        {
            return new ByteBuilder(this._items, 0, this._size);
        }

        public virtual bool Contains(byte value)
        {
            return (this.IndexOf(value, 0, this._size) > -1);
        }

        internal virtual bool Contains(byte value, int startIndex, int count)
        {
            return (this.IndexOf(value, startIndex, count) > -1);
        }

        public virtual void CopyTo(Array array)
        {
            Array.Copy(this._items, array, this._size);
        }

        public virtual void CopyTo(Array array, int index)
        {
            this.CopyTo(0, array, index, this._size);
        }

        public virtual void CopyTo(int index, Array array, int arrayIndex, int count)
        {
            if (array == null)
                throw new ArgumentNullException("array");
            
            if (array.Rank != 1)
                throw new ArgumentException("Must have only 1 dimensions.", "array");
            
            Array.Copy(this._items, index, array, arrayIndex, count);
        }

        private void EnsureCapacity(int count)
        {
            if (count > this._items.Length)
            {
                int num = this._items.Length << 1;
                if (num == 0)
                    num = 0x10;
                
                while (num < count)
                {
                    num = num << 1;
                }
                byte[] destinationArray = new byte[num];
                Array.Copy(this._items, 0, destinationArray, 0, this._items.Length);
                this._items = destinationArray;
            }
        }

        public virtual int IndexOf(byte[] value)
        {
            return this.IndexOf(value, 0);
        }

        public virtual int IndexOf(byte value)
        {
            return this.IndexOf(value, 0);
        }

        public virtual int IndexOf(byte value, int startIndex)
        {
            return this.IndexOf(value, startIndex, this._size - startIndex);
        }

        public virtual int IndexOf(byte[] value, int startIndex)
        {
            return this.IndexOf(value, startIndex, this._size - startIndex);
        }

        public virtual int IndexOf(byte[] value, int startIndex, int count)
        {
            if ((startIndex < 0) || (startIndex > this._size))
                throw new ArgumentOutOfRangeException("startIndex", startIndex, "Does not specify valid index.");
            
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", count, "Can't be less than 0.");
            
            if (startIndex > (this._size - count))
                throw new ArgumentOutOfRangeException("count", "Start index and count do not specify a valid range.");
            
            int index = 0;
            for (int i = startIndex; i < count; i++)
            {
                if (this._items[i] != value[index])
                    index = 0;
            
                else
                {
                    index++;
                    if (index == value.Length)
                        return ((startIndex + 1) + (i - index));
                }
            }
            return -1;
        }

        public virtual int IndexOf(byte value, int startIndex, int count)
        {
            if ((startIndex < 0) || (startIndex > this._size))
                throw new ArgumentOutOfRangeException("startIndex", startIndex, "Does not specify valid index.");
            
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", count, "Can't be less than 0.");
            
            if (startIndex > (this._size - count))
                throw new ArgumentOutOfRangeException("count", "Start index and count do not specify a valid range.");
            
            return Array.IndexOf(this._items, value, startIndex, count);
        }

        public virtual void Insert(int index, byte value)
        {
            if ((index < 0) || (index > this._size))
                throw new ArgumentOutOfRangeException("index", index, "Index must be >= 0 and <= Count.");
            
            this.Shift(index, 1);
            this._items[index] = value;
            this._size++;
            this._version++;
        }

        public virtual void InsertRange(int index, byte[] c)
        {
            if (c != null)
            {
                if ((index < 0) || (index > this._size))
                    throw new ArgumentOutOfRangeException("index", index, "Index must be >= 0 and <= Count.");
                
                int length = c.Length;
                
                if (this._items.Length < (this._size + length))
                    this.EnsureCapacity(this._size + length);
                
                if (index < this._size)
                    Array.Copy(this._items, index, this._items, index + length, this._size - index);
                
                if (this == c.SyncRoot)
                {
                    Array.Copy(this._items, 0, this._items, index, index);
                    Array.Copy(this._items, (int)(index + length), this._items, (int)(index << 1), (int)(this._size - index));
                }
                else
                    c.CopyTo(this._items, index);
                
                this._size += c.Length;
                this._version++;
            }
        }

        public virtual int LastIndexOf(byte value)
        {
            return this.LastIndexOf(value, this._size - 1);
        }

        public virtual int LastIndexOf(byte value, int startIndex)
        {
            return this.LastIndexOf(value, startIndex, startIndex + 1);
        }

        public virtual int LastIndexOf(byte value, int startIndex, int count)
        {
            return Array.LastIndexOf(this._items, value, startIndex, count);
        }

        public virtual byte ReadByte(ref int StartPosition)
        {
            byte num = this._items[StartPosition];
            StartPosition++;
            return num;
        }

        public virtual byte[] ReadBytes(ref int StartPosition, int Length)
        {
            byte[] buffer = this.ToArray(StartPosition, Length);
            StartPosition += Length;
            return buffer;
        }

        public virtual byte[] ReadBytesUntil(ref int StartPosition, byte UntilSearch)
        {
            int index = this.IndexOf(UntilSearch, StartPosition);
            if (index < 0)
                throw new Exception("Parsing error");
            
            int length = index - StartPosition;
            
            if (length < 1)
            {
                StartPosition++;
                return new byte[0];
            }
            byte[] buffer = this.ReadBytes(ref StartPosition, length);
            StartPosition++;
            return buffer;
        }

        public virtual void Remove(byte[] value)
        {
            int index = this.IndexOf(value);
            if (index > -1)
                this.RemoveRange(index, value.Length);
            
            this._version++;
        }

        public virtual void Remove(byte value)
        {
            int index = this.IndexOf(value);
            if (index > -1)
                this.RemoveAt(index);
            
            this._version++;
        }

        public virtual void RemoveAt(int index)
        {
            if ((index < 0) || (index >= this._size))
                throw new ArgumentOutOfRangeException("index", index, "Less than 0 or more than list count.");
            
            this.Shift(index, -1);
            this._size--;
            this._version++;
        }

        public virtual void RemoveRange(int index, int count)
        {
            CheckRange(index, count, this._size);
            this.Shift(index, -count);
            this._size -= count;
            this._version++;
        }

        public virtual void Reverse()
        {
            Array.Reverse(this._items, 0, this._size);
            this._version++;
        }

        public virtual void Reverse(int index, int count)
        {
            CheckRange(index, count, this._size);
            Array.Reverse(this._items, index, count);
            this._version++;
        }

        public virtual void SetRange(int index, ICollection c)
        {
            if (c == null)
                throw new ArgumentNullException("c");
            
            if ((index < 0) || ((index + c.Count) > this._size))
                throw new ArgumentOutOfRangeException("index");
            
            c.CopyTo(this._items, index);
            this._version++;
        }

        private void Shift(int index, int count)
        {
            if (count > 0)
            {
                if ((this._size + count) > this._items.Length)
                {
                    int num = (this._items.Length > 0) ? (this._items.Length << 1) : 1;
                    while (num < (this._size + count))
                    {
                        num = num << 1;
                    }
                    byte[] destinationArray = new byte[num];
                    Array.Copy(this._items, 0, destinationArray, 0, index);
                    Array.Copy(this._items, index, destinationArray, index + count, this._size - index);
                    this._items = destinationArray;
                }
                else
                    Array.Copy(this._items, index, this._items, index + count, this._size - index);
            }
            else if (count < 0)
            {
                int sourceIndex = index - count;
                Array.Copy(this._items, sourceIndex, this._items, index, this._size - sourceIndex);
            }
        }

        public virtual void Sort()
        {
            Array.Sort(this._items, 0, this._size);
            this._version++;
        }

        public virtual void Sort(IComparer comparer)
        {
            Array.Sort(this._items, 0, this._size, comparer);
        }

        public virtual void Sort(int index, int count, IComparer comparer)
        {
            CheckRange(index, count, this._size);
            Array.Sort(this._items, index, count, comparer);
        }

        public virtual byte[] ToArray()
        {
            return this.ToArray(0, this._size);
        }

        public virtual Array ToArray(Type elementType)
        {
            Array array = Array.CreateInstance(elementType, this._size);
            this.CopyTo(array);
            return array;
        }

        public virtual byte[] ToArray(int index, int count)
        {
            byte[] array = new byte[count];
            this.CopyTo(index, array, 0, count);
            return array;
        }

        public virtual void TrimToSize()
        {
            if (this._items.Length > this._size)
            {
                byte[] buffer;
                if (this._size == 0)
                    buffer = new byte[0x10];
                
                else
                    buffer = new byte[this._size];
                
                Array.Copy(this._items, 0, buffer, 0, this._size);
                this._items = buffer;
            }
        }

        // Properties
        public virtual int Capacity
        {
            get { return this._items.Length; }
            set
            {
                if (value < this._size)
                    throw new ArgumentOutOfRangeException("Capacity", value, "Must be more than count.");
                
                byte[] destinationArray = new byte[value];
                Array.Copy(this._items, 0, destinationArray, 0, this._size);
                this._items = destinationArray;
            }
        }

        public virtual int Count
        {
            get { return this._size; }
        }

        public virtual bool IsFixedSize
        {
            get { return false; }
        }

        public virtual bool IsReadOnly
        {
            get { return false; }
        }

        public virtual bool IsSynchronized
        {
            get { return false; }
        }

        public virtual byte this[int index]
        {
            get
            {
                if ((index < 0) || (index >= this._size))
                    throw new ArgumentOutOfRangeException("index", index, "Index is less than 0 or more than or equal to the list count.");
                
                return this._items[index];
            }
            set
            {
                if ((index < 0) || (index >= this._size))
                    throw new ArgumentOutOfRangeException("index", index, "Index is less than 0 or more than or equal to the list count.");
                
                this._items[index] = value;
                this._version++;
            }
        }

        public virtual object SyncRoot
        {
            get {  return this; }
        }
    }
}

